function drawtrees(gp,ID,reportName,font,nodeTextColor,connectionLineStyle,nodeBorderStyle,nodeColor1,nodeColor2,boxShadow)
%DRAWTREES Draws the tree structure(s) of an individual in a web browser.
%
%   DRAWTREES function will draw the 'trees' in the selected individual
%   regardless of whether it is a regression model or not. The style and
%   colours used to draw the trees can be controlled by additional CSS
%   arguments as outlined below.
%
%   DRAWTREES(GP,ID) draws the trees in trees.htm for the population member
%   with numeric identifer ID in the GPTIPS datastructure GP.
%
%   DRAWTREES(GP,ID,'REPORTNAME') draws the trees in REPORTNAME.htm.
%
%   DRAWTREES(GP,'best','REPORTNAME') does the same for the best individual
%   of the run (as evaluated on training data).
%
%   DRAWTREES(GP,'valbest','REPORTNAME') does the same for the individual
%   that performed best on the validation data (if this data exists).
%
%   DRAWTREES(GP,'testbest','REPORTNAME') does the same for the individual
%   that performed best on the test data (if this data exists).
%
%   DRAWTREES(GP,GPMODEL) operates on the GPMODEL struct representing a
%   multigene regression model, i.e. the struct returned by the functions
%   GPMODEL2STRUCT or GENES2GPMODEL.
%
%   DRAWTREES(GP,EXPR) where EXPR is a cell array of encoded tree strings
%   draws the corresponding trees. Each element of EXPR must be a valid
%   encoded tree expression such as that generated by TREEGEN. For example,
%   to draw 2 randomly generated trees:
%
%   EXPR{1} = TREEGEN(GP);EXPR{2} = TREEGEN(GP);
%   DRAWTREES(GP,EXPR);
%
%   Advanced formatting:
%
%   The default colours and formatting options are intended to be sober and
%   for use in publications etc. To change these you can supply the
%   following additional command line parameters.
%
%   DRAWTREES(GP,ID,REPORTNAME,FONT,NODETEXTCOLOR,CONNECTIONLINESTYLE,NODEBORDERSTYLE,NODECOLOR1,NODECOLOR2,BOXSHADOW)
%
%   Valid CSS styles should be used as the additional parameters as
%   follows. Any parameter left empty ([]) will use the defaults.
%
%   FONT should be a valid CSS font or font-family, e.g. arial or 'Open
%   Sans'. As well as the standard web safe fonts, popular 'google fonts'
%   supported are: 'Open Sans','Roboto','Oxygen' and 'PT Sans'. This
%   specifies the font used in the tree nodes and the font used in the
%   report text. The default is 'Open Sans'.
%
%   NODETEXTCOLOR is a string containing CSS for the node text color, e.g.
%   'black' or '#ED145A'. The default is 'black'.
%
%   CONNECTIONLINESTYLE is a string containing CSS for the lines connecting
%   the nodes, e.g. '2px solid black' or '1px solid #ED145A'. The default
%   is '2px solid black'.
%
%   NODEBORDERSTYLE is a string containing CSS for the node borders e.g.
%   '2px solid blue' or '5px solid #18D8F0'. The default is '2px solid
%   black'.
%
%   NODECOLOR 1 and NODECOLOR2 are strings containing CSS colours, e.g.
%   #18D8F0' or 'blue'. The node is drawn with a linear gradient fill from
%   top to bottom starting with NODECOLOR1 and ending with NODECOLOR2. The
%   defaults are 'white'.
%
%   BOXSHADOW is a boolean with TRUE = use a node box shadow and FALSE = no
%   shadow. The default is FALSE.
%
%   Examples:
%
%   To use all the style defaults, but to change the font to 'Roboto' use
%
%   drawtrees(gp,'best',[],'Roboto')
%
%   For a highly not recommended 'Miami Vice' vibe use
%
%   drawtrees(gp,'best',[],'Comic Sans MS','yellow','8px solid pink','3px solid #ED145A','#18D8F0','#F090C0',true)
%
%   Remarks:
%
%   This function connects to the Google Visualization API and hence
%   internet connectivity is required.
%
%   See the following link for details of Google org charts:
%   https://developers.google.com/chart/interactive/docs/gallery/orgchart
%
%   Copyright (c) 2009-2015 Dominic Searson
%
%   GPTIPS 2
%
%   See also PROCESSORGCHARTJS, GPTREESTRUCTURE, GPMODELREPORT

if nargin < 2
    disp('Basic usage is DRAWTREES(GP,ID) where ID is a population member identifier, e.g.');
    disp('DRAWTREES(GP,26) or');
    disp('DRAWTREES(GP,''BEST'')');
    disp('DRAWTREES(GP,''VALBEST'')');
    disp('DRAWTREES(GP,''TESTBEST'')');
    return;
end

if nargin < 3 || isempty(reportName)
    reportName = 'trees';
end

if nargin < 4 || isempty(font)
    font = 'Open Sans';
end

if nargin < 5 || isempty(nodeTextColor)
    nodeTextColor = 'black';
end

if nargin < 6 || isempty(connectionLineStyle)
    connectionLineStyle= '2px solid black';
end

if nargin < 7 || isempty(nodeBorderStyle)
    nodeBorderStyle = '2px solid black';
end

if nargin < 8 || isempty(nodeColor1)
    nodeColor1 = 'white';
end

if nargin < 9 || isempty(nodeColor2)
    nodeColor2 = 'white';
end

if nargin < 10 || isempty(boxShadow)
    boxShadow = false;
end

%extract data set name if defined
if ~isempty(gp.userdata.name)
    setname = ['Data: ' gp.userdata.name];
else
    setname = '';
end

%gpmodel struct supplied
if isa(ID,'struct') && isfield(ID,'valid')
    
    gpmodel = ID;
    dispId = 'user model';
    
    %numeric population index supplied
elseif isnumeric(ID)
    
    if ID > gp.runcontrol.pop_size || ID < 1
        error('Supplied numeric individual identifier is invalid.');
    end
    
    gpmodel.genes.geneStrs = gp.pop{ID};
    gpmodel.genes.num_genes =  numel(gpmodel.genes.geneStrs);
    dispId = num2str(ID);
    
elseif ischar(ID) && strcmpi(ID,'best')
    
    gpmodel.genes.geneStrs = gp.results.best.individual;
    gpmodel.genes.num_genes =  numel(gpmodel.genes.geneStrs);
    dispId = ID;
    
elseif ischar(ID) && strcmpi(ID,'valbest')
    
    % check that validation data is present
    if ~isfield(gp.results,'valbest')
        disp('No validation data was found. Try drawtrees(gp,''best'') instead.');
        return;
    end
    
    gpmodel.genes.geneStrs = gp.results.valbest.individual;
    gpmodel.genes.num_genes =  numel(gpmodel.genes.geneStrs);
    dispId = ID;
    
elseif ischar(ID) && strcmpi(ID,'testbest')
    
    % check that validation data is present
    if ~isfield(gp.results,'testbest')
        disp('No test data was found. Try drawtrees(gp,''best'') instead.');
        return;
    end
    
    gpmodel.genes.geneStrs = gp.results.testbest.individual;
    gpmodel.genes.num_genes =  numel(gpmodel.genes.geneStrs);
    dispId = ID;
    
    %cell array of encoded tree strings
elseif iscell(ID) && ischar(ID{1})
    
    gpmodel.genes.geneStrs = ID;
    gpmodel.genes.num_genes = numel(ID);
    dispId = 'Cell array of encoded trees';
    
    %single encoded tree string
elseif ischar(ID)
    
    gpmodel.genes.geneStrs = {ID};
    gpmodel.genes.num_genes = 1;
    dispId = ID;
    
else
    error('Invalid selection.');
end

%create html file
if ~ischar(reportName)
    error('The reportname parameter must be a string.');
end

htmlFileName = [reportName '.htm'];
fid = fopen(htmlFileName,'wt+');

if fid == -1
    error(['Could not open the file ' htmlFileName ' for writing.']);
end

%generate html header info
fprintf(fid,'<!DOCTYPE html>');
fprintf(fid,'\n');
fprintf(fid,'<html lang="en">\n');
fprintf(fid,'<head>\n');
fprintf(fid,'<meta http-equiv="content-type" content="text/html; charset=utf-8" name="description" content="GPTIPS 2 Trees" name="author" content="Dominic Searson"/>\n');
fprintf(fid,['<title>GPTIPS Trees. Config: ' char(gp.info.configFile) '. Trees from individual: ' dispId '</title>\n']);

%popular google fonts
fprintf(fid,'<link href=''http://fonts.googleapis.com/css?family=Open+Sans'' rel=''stylesheet'' type=''text/css''>');
fprintf(fid,'<link href=''http://fonts.googleapis.com/css?family=Roboto'' rel=''stylesheet'' type=''text/css''>');
fprintf(fid,'<link href=''http://fonts.googleapis.com/css?family=Oxygen'' rel=''stylesheet'' type=''text/css''>');
fprintf(fid,'<link href=''http://fonts.googleapis.com/css?family=PT+Sans'' rel=''stylesheet'' type=''text/css''>');

%load vis from Google
fprintf(fid,'<script type="text/javascript" src="http://www.google.com/jsapi"></script>\n');
fprintf(fid,'<script type="text/javascript">google.load(''visualization'', ''1'', {packages: [''orgchart'']});\n');
fprintf(fid,'</script>\n');

%node CSS
fprintf(fid,'<style>');
fprintf(fid,'.google-visualization-orgchart-node {');
fprintf(fid,['color: ' nodeTextColor ' ;']);
fprintf(fid,'text-align: center;');
fprintf(fid,'vertical-align: middle;');
fprintf(fid,['font-family: ''' font  ''',''Helvetica Neue'',helvetica,arial,sans-serif;']);
fprintf(fid,['border: ' nodeBorderStyle ' ;']);
fprintf(fid,['background-color: ' nodeColor1 ';']);
fprintf(fid,['background: -webkit-gradient(linear, left top, left bottom, from(' nodeColor1 '), to(' nodeColor2 '));']);
fprintf(fid,'vertical-align: middle;');

if ~boxShadow
    fprintf(fid,'box-shadow: none;');
    fprintf(fid,'-webkit-box-shadow: none;');
    fprintf(fid,'-moz-box-shadow: none;');
end

fprintf(fid,'}');
fprintf(fid,'h1, h2, h3 {\n');
fprintf(fid,'color: #0073bd; ');
fprintf(fid,'margin-top: 20px; ');
fprintf(fid,'\n}\n');
fprintf(fid,'</style>');

%connecting line CSS
fprintf(fid,'<style>');
fprintf(fid,'.google-visualization-orgchart-lineleft {');
fprintf(fid,[' border-left: ' connectionLineStyle ' ; }']);
fprintf(fid,'.google-visualization-orgchart-lineright {');
fprintf(fid,[' border-right: ' connectionLineStyle ' ; }']);
fprintf(fid,'.google-visualization-orgchart-linebottom {');
fprintf(fid,[' border-bottom: ' connectionLineStyle ' ; }']);
fprintf(fid,'</style>');

%generate JS for tree charts
processOrgChartJS(fid,gp,gpmodel);

%end head
fprintf(fid,'</head>');

%generate html body
fprintf(fid,['<body style="font-family: ''' font ''',''Helvetica Neue'', helvetica, arial, sans-serif; ">']);
fprintf(fid,'<div style="text-align: left;margin-bottom: 30px; margin-top: 30px;margin-left: 30px;">');

fprintf(fid,'<h2>GPTIPS tree structure report</h2>');

if ~isempty(setname)
    fprintf(fid,['<p class="text">' setname '</p>\n']);
end

if isnumeric(ID) && ID > 0 && ID <= gp.runcontrol.pop_size
    fprintf(fid,['<p class="text">For individual with ID: ' int2str(ID) '</p>\n']);
elseif strcmpi(ID,'best')
    fprintf(fid,'<p class="text">For best model on training data.</p>\n');
elseif strcmpi(ID,'valbest');
    fprintf(fid,'<p class="text">For best model on validation data.</p>\n');
elseif strcmpi(ID,'testbest');
    fprintf(fid,'<p class="text">For best model on test data.</p>\n');
elseif isa(ID,'struct') && isfield(ID,'valid')
    fprintf(fid,'<p class="text">For a user generated model struct.</p>\n');
end
fprintf(fid,['<p class="date">' datestr(now) '</p>\n']);

%gene tree structures
for n=1:gpmodel.genes.num_genes
    fprintf(fid,'<table>');
    fprintf(fid,'<tr>');
    fprintf(fid,'<td style="text-align:center;">');
    fprintf(fid,['<p>Tree ' int2str(n) '</p><p style="color:gray;"> nodes = ' int2str(getnumnodes(gpmodel.genes.geneStrs{n}))...
        ' depth = ' int2str(getdepth(gpmodel.genes.geneStrs{n})) ' complexity = ' int2str(getcomplexity(gpmodel.genes.geneStrs{n}))   '</p>\n']);
    fprintf(fid,'</td>');
    fprintf(fid,'</tr>');
    fprintf(fid,'<tr>');
    fprintf(fid,'<td>');
    fprintf(fid,['<div id="tree' int2str(n) '" style="width: 300px;"></div>\n']);
    fprintf(fid,'</td>');
    fprintf(fid,'</tr>');
    fprintf(fid,'</table>');
    fprintf(fid,'<p></p>\n');
end

fprintf(fid,'</div>');

%footer
fprintf(fid,'<p>&nbsp;</p>\n');
fprintf(fid,'<p>&nbsp;</p>\n');
fprintf(fid,'<p style="color:gray;text-align:center;">GPTIPS - the symbolic data mining platform for MATLAB</p>');
fprintf(fid,'<p style="color:gray;text-align:center;">&#169; Dominic Searson 2009-2015</p>');

%close
fprintf(fid,'</body>\n');
fprintf(fid,'</html>\n');
fclose(fid);

disp(['Trees drawn to ' reportName '.htm']);
disp('Opening in system browser.');
web(htmlFileName,'-browser');